
//package java_cup;

//import System.IO.*;
//import java_cup.runtime.*;

/**
 * This class serves as the main driver for the JavaCup system. It accepts user
 * options and coordinates overall control flow. The main flow of control
 * includes the following activities:
 * <ul>
 * <li>Parse user supplied arguments and options.
 * <li>Open output files.
 * <li>Parse the specification from standard input.
 * <li>Check for unused terminals, non-terminals, and AllProdctions.
 * <li>Build the state machine, tables, etc.
 * <li>Output the generated code.
 * <li>Close output files.
 * <li>Print a summary if requested.
 * </ul>
 *
 * Options to the main program include:
 * <dl>
 * <dt>-//package name
 * <dd>specify //package generated classes go in [default none]
 * <dt>-CUPParser name
 * <dd>specify CUPParser class name [default "CUPParser"]
 * <dt>-EmitSymbols name
 * <dd>specify name for SymbolDraft constant class [default "symbolCount"]
 * <dt>-interface
 * <dd>emit SymbolDraft constant <i>interface</i>, rather than class
 * <dt>-nonterms
 * <dd>put non terminals in SymbolDraft constant class
 * <dt>-expect #
 * <dd>Count of conflicts expected/allowed [default 0]
 * <dt>-compact_red
 * <dd>compact tables by defaulting to most frequent reduce
 * <dt>-nowarn
 * <dd>don't warn about useless AllProdctions, etc.
 * <dt>-nosummary
 * <dd>don't print the usual summary of parse states, etc.
 * <dt>-progress
 * <dd>print messages to indicate progress of the system
 * <dt>-time
 * <dd>print time usage summary
 * <dt>-dump_grammar
 * <dd>produce a dump of the EmitSymbols and grammar
 * <dt>-dump_states
 * <dd>produce a dump of parse state machine
 * <dt>-dump_tables
 * <dd>produce a dump of the parse tables
 * <dt>-dump
 * <dd>produce a dump of AllVns of the above
 * <dt>-debug
 * <dd>turn on debugging messages within JavaCup
 * <dt>-nopositions
 * <dd>don't generate the positions code
 * <dt>-locations
 * <dd>generate handles xleft/xright for SymbolDraft positions in actions
 * <dt>-noscanner
 * <dd>don't refer to java_cup.runtime.Scanner in the CUPParser (for compatibility
 * with old runtimes)
 * <dt>-version
 * <dd>print version information for JavaCUP and halt.
 * </dl>
 *
 * @version last updated: 7/3/96
 * @author Frank Flannery
 */

public class TheMain {

    /*-----------------------------------------------------------*/
    /*--- Constructor(s) ----------------------------------------*/
    /*-----------------------------------------------------------*/
    /**
     * Only constructor is public, so we do not allocate any instances of this
     * class.
     */
    public TheMain() {
    }

    /*-------------------------*/
    /* Options set by the user */
    /*-------------------------*/
    /** User option -- do we print progress messages. */
    public static bool print_progress = false;
    /** User option -- do we produce a dump of the state machine */
    public static bool opt_dump_states = false;
    /** User option -- do we produce a dump of the parse tables */
    public static bool opt_dump_tables = false;
    /** User option -- do we produce a dump of the grammar */
    public static bool opt_dump_grammar = false;
    /** User option -- do we show timing information as a part of the summary */
    public static bool opt_show_timing = false;
    /** User option -- do we run produce extra debugging messages */
    public static bool opt_do_debug = false;
    /** User option -- do eclipse debug EmitSymbols */
    public static bool opt_do_debugsymbols = false;
    /**
     * User option -- do we compact tables by making most common reduce the default
     * actionPart
     */
    public static bool opt_compact_red = false;
    /**
     * User option -- should we include non terminal SymbolDraft numbers in the SymbolDraft
     * constant class.
     */
    public static bool include_non_terms = false;
    /** User option -- do not print a summary. */
    public static bool no_summary = false;
    /** User option -- Count of conflicts to expect */
    public static int expect_conflicts = 0;

    /* frankf added this 6/18/96 */
    /** User option -- should generator generate code for _left/_right values? */
    public static bool lr_values = true;
    public static bool locations = false;
    public static bool xmlactions = false;
    public static bool genericlabels = false;

    /** User option -- should EmitSymbols be put in a class or an interface? [CSA] */
    public static bool sym_interface = false;

    /**
     * User option -- should generator suppress references to
     * java_cup.runtime.Scanner for compatibility with old runtimes?
     */
    public static bool suppress_scanner = false;

    /*----------------------------------------------------------------------*/
    /* Timing data (not AllVns of these time intervals are mutually exclusive) */
    /*----------------------------------------------------------------------*/
    /** Timing data -- when did we start */
    public static long start_time = 0;
    /** Timing data -- when did we end preliminaries */
    public static long prelim_end = 0;
    /** Timing data -- when did we end parsing */
    public static long parse_end = 0;
    /** Timing data -- when did we end checking */
    public static long check_end = 0;
    /** Timing data -- when did we end dumping */
    public static long dump_end = 0;
    /** Timing data -- when did we end state and table building */
    public static long build_end = 0;
    /** Timing data -- when did we end nullable calculation */
    public static long nullability_end = 0;
    /** Timing data -- when did we end first set calculation */
    public static long first_end = 0;
    /** Timing data -- when did we end state machine construction */
    public static long machine_end = 0;
    /** Timing data -- when did we end table construction */
    public static long table_end = 0;
    /** Timing data -- when did we end checking for non-reduced AllProdctions */
    public static long reduce_check_end = 0;
    /** Timing data -- when did we finish emitting code */
    public static long emit_end = 0;
    /** Timing data -- when were we completely done */
    public static long final_time = 0;

    /* Additional timing information is also collected in emit */

    /*-----------------------------------------------------------*/
    /*--- TheMain Program ------------------------------------------*/
    /*-----------------------------------------------------------*/

    /**
     * The main driver for the system.
     * 
     * @param argv an array of strings containing command line arguments.
     */
    public static void Main(String[] argv) /*throws internal_error, System.IO.IOException, java.lang.ExceptionXml*/ {
        bool did_output = false;

        start_time = System.DateTime.Now.Millisecond;

        /** clean AllVns static members, that contain remaining stuff from earlier calls */
        Vt.clear();
        ProductionDraft.Clear();
        ActionProduction.Clear();
        Emit.clear();
        Vn.clear();
        ParseReduceRow.clear();
        ParseActionRow.clear();
        LALRState.Clear();

        /* process user options and arguments */
        parseArgs(argv);

        /*
         * frankf 6/18/96 hackish, yes, but works
         */
        Emit.setLrValues(lr_values);
        Emit.SetLocations(locations);
        Emit.setXmlactions(xmlactions);
        Emit.setGenericlabels(genericlabels);
        /* open output set_xmlactionsfiles */
        if (print_progress)
            System/*error*/.Console.WriteLine("Opening files...");
        /* use a buffered version of standard input */
        input_file = System.Console.In;

        prelim_end = System.DateTime.Now.Millisecond;

        /* parse spec into internal data structures */
        if (print_progress)
            System/*error*/.Console.WriteLine("Parsing specification from standard input...");
        //parseGrammarSpec();

        parse_end = System.DateTime.Now.Millisecond;

        /* don't proceed unless we are error free */
        if (ErrorManager.instance.ErrorCount == 0) {
            /* check for unused bits */
            if (print_progress)
                System/*error*/.Console.WriteLine("Checking specification...");
            checkUnused();

            check_end = System.DateTime.Now.Millisecond;

            /* build the state machine and parse tables */
            if (print_progress)
                System/*error*/.Console.WriteLine("Building parse tables...");
            buildParser();

            build_end = System.DateTime.Now.Millisecond;

            /* output the generated code, if # of conflicts permits */
            if (ErrorManager.instance.ErrorCount != 0) {
                // conflicts! don't emit code, don't dump tables.
                opt_dump_tables = false;
            }
            else { // everything's okay, emit CUPParser.
                if (print_progress)
                    System/*error*/.Console.WriteLine("Writing CUPParser...");
                openFiles();
                emitParser();
                did_output = true;
            }
        }
        /* fix up the times to make the summary easier */
        emit_end = System.DateTime.Now.Millisecond;

        /* do requested dumps */
        if (opt_dump_grammar)
            dumpGrammar();
        if (opt_dump_states)
            dumpMachine();
        if (opt_dump_tables)
            dumpTables();

        dump_end = System.DateTime.Now.Millisecond;

        /* close input/output files */
        if (print_progress)
            System/*error*/.Console.WriteLine("Closing files...");
        closeFiles();

        /* produce a summary if desired */
        if (!no_summary)
            emitSummary(did_output);

        /*
         * If there were errors during the run, exit with non-zero status
         * (makefile-friendliness). --CSA
         */
        //if (ErrorManager.getManager().getErrorCount() != 0)
        //System.exit(100);
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /**
     * Print a "usage message" that described possible command line options, then
     * exit.
     * 
     * @param message a specific error message to preface the usage message by.
     */
    public static void usage(String message) {
        System/*error*/.Console.WriteLine();
        System/*error*/.Console.WriteLine(message);
        System/*error*/.Console.WriteLine();
        System/*error*/.Console.WriteLine(Version.titleStr + "\n" + "Usage: " + Version.programName + " [options] [filename]\n"
            + "  and expects a specification file on standard input if no filename is given.\n"
            + "  Legal options include:\n" + "    -//package name  specify //package generated classes go in [default none]\n"
            + "    -destdir name  specify the destination directory, to store the generated files in\n"
            + "    -CUPParser name   specify CUPParser class name [default \"CUPParser\"]\n"
            + "    -typearg args  specify type arguments for CUPParser class\n"
            + "    -EmitSymbols name  specify name for SymbolDraft constant class [default \"symbolCount\"]\n"
            + "    -interface     put EmitSymbols in an interface, rather than a class\n"
            + "    -nonterms      put non terminals in SymbolDraft constant class\n"
            + "    -expect #      Count of conflicts expected/allowed [default 0]\n"
            + "    -compact_red   compact tables by defaulting to most frequent reduce\n"
            + "    -nowarn        don't warn about useless AllProdctions, etc.\n"
            + "    -nosummary     don't print the usual summary of parse states, etc.\n"
            + "    -nopositions   don't propagate the _left and _right token position values\n"
            + "    -locations     generate handles xleft/xright for SymbolDraft positions in actions\n"
            + "    -xmlactions    make the generated CUPParser yield its parse tree as XML\n"
            + "    -genericlabels automatically generate labels to AllVns EmitSymbols in XML mode\n"
            + "    -noscanner     don't refer to java_cup.runtime.Scanner\n"
            + "    -progress      print messages to indicate progress of the system\n"
            + "    -time          print time usage summary\n"
            + "    -dump_grammar  produce a human readable dump of the EmitSymbols and grammar\n"
            + "    -dump_states   produce a dump of parse state machine\n"
            + "    -dump_tables   produce a dump of the parse tables\n"
            + "    -dump          produce a dump of AllVns of the above\n"
            + "    -version       print the version information for CUP and exit\n");
        ////System.exit(1);
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /**
     * Parse command line options and arguments to set various user-option flags and
     * variables.
     * 
     * @param argv the command line arguments to be parsed.
     */
    public static void parseArgs(String[] argv) {
        int len = argv.Length;
        int i;

        /* parse the options */
        for (i = 0; i < len; i++) {
            /* try to get the various options */
            if (argv[i].Equals("-//package")) {
                /* must have an arg */
                if (++i >= len || argv[i].StartsWith("-") || argv[i].EndsWith(".cup"))
                    usage("-//package must have a name argument");

                /* record the name */
                Emit.package_name = argv[i];
            }
            else if (argv[i].Equals("-destdir")) {
                /* must have an arg */
                if (++i >= len || argv[i].StartsWith("-") || argv[i].EndsWith(".cup"))
                    usage("-destdir must have a name argument");
                /* record the name */
                global::TheMain.dest_dir = new System.IO.FileInfo(argv[i]);
            }
            else if (argv[i].Equals("-CUPParser")) {
                /* must have an arg */
                if (++i >= len || argv[i].StartsWith("-") || argv[i].EndsWith(".cup"))
                    usage("-CUPParser must have a name argument");

                /* record the name */
                Emit.parser_class_name = argv[i];
            }
            else if (argv[i].Equals("-EmitSymbols")) {
                /* must have an arg */
                if (++i >= len || argv[i].StartsWith("-") || argv[i].EndsWith(".cup"))
                    usage("-EmitSymbols must have a name argument");

                /* record the name */
                Emit.symbol_const_class_name = argv[i];
            }
            else if (argv[i].Equals("-nonterms")) {
                include_non_terms = true;
            }
            else if (argv[i].Equals("-expect")) {
                /* must have an arg */
                if (++i >= len || argv[i].StartsWith("-") || argv[i].EndsWith(".cup"))
                    usage("-expect must have a name argument");

                /* record the Count */
                try {
                    expect_conflicts = int.Parse(argv[i]);
                }
                catch (Exception e) {
                    usage("-expect must be followed by a decimal integer");
                }
            }
            else if (argv[i].Equals("-compact_red"))
                opt_compact_red = true;
            else if (argv[i].Equals("-nosummary"))
                no_summary = true;
            else if (argv[i].Equals("-nowarn"))
                Emit.nowarn = true;
            else if (argv[i].Equals("-dump_states"))
                opt_dump_states = true;
            else if (argv[i].Equals("-dump_tables"))
                opt_dump_tables = true;
            else if (argv[i].Equals("-progress"))
                print_progress = true;
            else if (argv[i].Equals("-dump_grammar"))
                opt_dump_grammar = true;
            else if (argv[i].Equals("-dump"))
                opt_dump_states = opt_dump_tables = opt_dump_grammar = true;
            else if (argv[i].Equals("-time"))
                opt_show_timing = true;
            else if (argv[i].Equals("-debug"))
                opt_do_debug = true;
            else if (argv[i].Equals("-debugsymbols"))
                opt_do_debugsymbols = true;
            /* frankf 6/18/96 */
            else if (argv[i].Equals("-nopositions"))
                lr_values = false;
            else if (argv[i].Equals("-locations"))
                locations = true;
            else if (argv[i].Equals("-xmlactions"))
                xmlactions = true;
            else if (argv[i].Equals("-genericlabels"))
                genericlabels = true;
            /* CSA 12/21/97 */
            else if (argv[i].Equals("-interface"))
                sym_interface = true;
            /* CSA 23-Jul-1999 */
            else if (argv[i].Equals("-noscanner"))
                suppress_scanner = true;
            /* CSA 23-Jul-1999 */
            else if (argv[i].Equals("-version")) {
                System.Console.WriteLine(Version.titleStr);
                //System.exit(1);
            }
            /* TUM changes; suggested by Henning Niss 20050628 */
            else if (argv[i].Equals("-typearg")) {
                if (++i >= len || argv[i].StartsWith("-") || argv[i].EndsWith(".cup"))
                    usage("-EmitSymbols must have a name argument");

                /* record the typearg */
                Emit.class_type_argument = argv[i];
            }

            /* CSA 24-Jul-1999; suggestion by Jean Vaucher */
            else if (!argv[i].StartsWith("-") && i == len - 1) {
                /* use input from file. */
                try {
                    System.Console.SetIn(new StreamReader(argv[i]));
                }
                catch (FileNotFoundException e) {
                    usage("Unable to open \"" + argv[i] + "\" for input");
                }
            }
            else {
                usage("Unrecognized option \"" + argv[i] + "\"");
            }
        }
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /*-------*/
    /* Files */
    /*-------*/

    /** Input file. This is a buffered version of System.in. */
    public static TextReader input_file;

    /** Output file for the CUPParser class. */
    public static TextWriter parser_class_file;

    /** Output file for the SymbolDraft constant class. */
    public static TextWriter symbol_class_file;

    /** Output directory. */
    public static FileInfo dest_dir = null;
    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /** Open various files used by the system. */
    public static void openFiles() {
        FileInfo fil;
        String out_name;

        /* open each of the output files */

        /* CUPParser class */
        out_name = Emit.parser_class_name + ".java";
        fil = new FileInfo(Path.Combine(dest_dir.FullName, out_name));
        try {
            parser_class_file = new StreamWriter(fil.FullName);
        }
        catch (Exception e) {
            System/*error*/.Console.WriteLine("Can't open \"" + out_name + "\" for output");
            //System.exit(3);
        }

        /* SymbolDraft constants class */
        out_name = Emit.symbol_const_class_name + ".java";
        try {
            symbol_class_file = new StreamWriter(fil.FullName);
        }
        catch (Exception e) {
            System/*error*/.Console.WriteLine("Can't open \"" + out_name + "\" for output");
            //System.exit(4);
        }
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /** Close various files used by the system. */
    public static void closeFiles() /*throws System.IO.IOException*/ {
        if (input_file != null)
            input_file.Close();
        if (parser_class_file != null)
            parser_class_file.Close();
        if (symbol_class_file != null)
            symbol_class_file.Close();
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /**
     * Parse the grammar specification from standard input. This produces sets of
     * terminal, non-terminals, and AllProdctions which can be accessed via static
     * variables of the respective classes, as well as the setting of various
     * variables (mostly in the emit class) for small user supplied ItemSet such as
     * the code to scan with.
     */
    public static void parseGrammarSpec() /*throws java.lang.ExceptionXml*/ {
        LRParser parser_obj;

        /* create a CUPParser and parse with it */
        ComplexSymbolFactory csf = new ComplexSymbolFactory();
        //parser_obj = new CUPParser(new Lexer(System.Console.In, csf), csf);
        parser_obj = new CUPParser(csf);
        //parser_obj.setDebugSymbols(opt_do_debugsymbols);
        try {
            if (opt_do_debug)
                parser_obj.debugParse();
            else
                parser_obj.parse();
        }
        catch (Exception e) {
            /*
             * something threw an exception. catch it and emit a message so we have a line
             * Count to work with, then re-throw it
             */
            //ErrorManager.getManager().emit_error("Internal error: Unexpected exception");
            throw e;
        }
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /**
     * Check for unused EmitSymbols. Unreduced AllProdctions get checked when tables are
     * created.
     */
    public static void checkUnused() {

        /* check for unused terminals */
        foreach (var term in Vt.AllTerminals()) {
            /* don't issue a message for EOF */
            if (term == Vt.EOF)
                continue;

            /* or error */
            if (term == Vt.error)
                continue;

            /* is this one unused */
            if (term.UseCount == 0) {
                /* count it and warn if we are doing warnings */
                Emit.unused_term++;
                if (!Emit.nowarn) {
                    //ErrorManager.getManager().emit_warning("TerminalXml \"" + term.name + "\" was declared but never used");
                }
            }
        }

        /* check for unused non terminals */
        foreach (var nt in Vn.AllVns()) {
            /* is this one unused */
            if (nt.UseCount == 0) {
                /* count and warn if we are doing warnings */
                Emit.unused_term++;
                if (!Emit.nowarn) {
                    //ErrorManager.getManager().emit_warning("Non terminal \"" + nt.name + "\" was declared but never used");
                }
            }
        }

    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . */
    /* . . Internal Results of Generating the Parser . . */
    /* . . . . . . . . . . . . . . . . . . . . . . . . . */

    /** Start state in the overall state machine. */
    public static LALRState start_state;

    /** Resulting parse actionPart table. */
    public static ParseActionTable action_table;

    /** Resulting reduce-goto table. */
    public static ParseReduceTable reduce_table;

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /**
     * Build the (internal) CUPParser from the previously parsed specification. This
     * includes:
     * <ul>
     * <li>Computing nullable of non-terminals.
     * <li>Computing first sets of non-terminals and AllProdctions.
     * <li>Building the viable prefix recognizer machine.
     * <li>Filling in the (internal) parse tables.
     * <li>Checking for unreduced AllProdctions.
     * </ul>
     */
    public static void buildParser() /*throws internal_error*/ {
        /* compute nullable of AllVns non terminals */
        if (opt_do_debug || print_progress)
            System/*error*/.Console.WriteLine("  Computing non-terminal nullable...");
        Vn.computeNullable();

        nullability_end = System.DateTime.Now.Millisecond;

        /* compute first sets of AllVns non terminals */
        if (opt_do_debug || print_progress)
            System/*error*/.Console.WriteLine("  Computing first sets...");
        Vn.computeFIRSTSets();

        first_end = System.DateTime.Now.Millisecond;

        /* build the LR viable prefix recognition machine */
        if (opt_do_debug || print_progress)
            System/*error*/.Console.WriteLine("  Building state machine...");
        start_state = LALRState.BuildMachine(Emit.firstProduction);

        machine_end = System.DateTime.Now.Millisecond;

        /* build the LR CUPParser actionPart and reduce-goto tables */
        if (opt_do_debug || print_progress)
            System/*error*/.Console.WriteLine("  Filling in tables...");
        action_table = new ParseActionTable();
        reduce_table = new ParseReduceTable();
        foreach (var state in LALRState.AllStates()) {
            state.BuildTableEntries(action_table, reduce_table);
        }

        table_end = System.DateTime.Now.Millisecond;

        /* check and warn for non-reduced AllProdctions */
        if (opt_do_debug || print_progress)
            System/*error*/.Console.WriteLine("  Checking for non-reduced AllProdctions...");
        action_table.checkReductions();

        reduce_check_end = System.DateTime.Now.Millisecond;

        /* if we have more conflicts than we expected issue a message and die */
        if (Emit.num_conflicts > expect_conflicts) {
            //ErrorManager.getManager()
            //    .emit_error("*** More conflicts encountered than expected " + "-- CUPParser generation aborted");
            //// indicate the problem.
            //// we'll die on return, after clean up.
        }
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /** Call the emit routines necessary to write @out the generated CUPParser. */
    public static void emitParser() /*throws internal_error*/ {
        Emit.EmitSymbols(symbol_class_file, include_non_terms, sym_interface);
        Emit.parser(parser_class_file, action_table, reduce_table, start_state.Index, Emit.firstProduction,
            opt_compact_red, suppress_scanner);
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /**
     * Helper routine to optionally return a plural or non-plural ending.
     * 
     * @param val the numerical value determining plurality.
     */
    public static String plural(int val) {
        if (val == 1)
            return "";
        else
            return "s";
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /**
     * Emit a long summary message to standard error (System.err) which summarizes
     * what was found in the specification, how many states were produced, how many
     * conflicts were found, etc. A detailed timing summary is also produced if it
     * was requested by the user.
     * 
     * @param output_produced did the system get far enough to generate code.
     */
    public static void emitSummary(bool outputProduced) {
        final_time = System.DateTime.Now.Millisecond;

        if (no_summary)
            return;

        System/*error*/.Console.WriteLine("------- " + Version.titleStr + " Parser Generation Summary -------");

        ///* error and warning count */
        //System/*error*/.Console.WriteLine("  " + ErrorManager.getManager().getErrorCount() + " error"
        //    + plural(ErrorManager.getManager().getErrorCount()) + " and " + ErrorManager.getManager().getWarningCount()
        //    + " warning" + plural(ErrorManager.getManager().getWarningCount()));

        /* basic stats */
        System/*error*/.Console.Write("  " + Vt.Count + " terminal" + plural(Vt.Count) + ", ");
        System/*error*/.Console.Write(Vn.Count + " non-terminal" + plural(Vn.Count) + ", and ");
        System/*error*/.Console.WriteLine(ProductionDraft.Count + " production" + plural(ProductionDraft.Count) + " declared, ");
        System/*error*/.Console.WriteLine("  producing " + LALRState.Count + " unique parse states.");

        /* unused EmitSymbols */
        System/*error*/.Console.WriteLine("  " + Emit.unused_term + " terminal" + plural(Emit.unused_term) + " declared but not used.");
        System/*error*/.Console.WriteLine("  " + Emit.unused_non_term + " non-terminal" + plural(Emit.unused_term) + " declared but not used.");

        /* AllProdctions that didn't reduce */
        System/*error*/.Console.WriteLine("  " + Emit.not_reduced + " production" + plural(Emit.not_reduced) + " never reduced.");

        /* conflicts */
        System/*error*/.Console.WriteLine("  " + Emit.num_conflicts + " conflict" + plural(Emit.num_conflicts) + " detected" + " ("
            + expect_conflicts + " expected).");

        /* code location */
        if (outputProduced)
            System/*error*/.Console.WriteLine("  Code written to \"" + Emit.parser_class_name + ".java\", and \""
                + Emit.symbol_const_class_name + ".java\".");
        else
            System/*error*/.Console.WriteLine("  No code produced.");

        if (opt_show_timing)
            showTimes();

        System/*error*/.Console.WriteLine("---------------------------------------------------- (" + Version.titleStr + ")");
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /** Produce the optional timing summary as part of an overall summary. */
    public static void showTimes() {
        long total_time = final_time - start_time;

        System/*error*/.Console.WriteLine(". . . . . . . . . . . . . . . . . . . . . . . . . ");
        System/*error*/.Console.WriteLine("  Timing Summary");
        System/*error*/.Console.WriteLine("    Total time       " + timestr(final_time - start_time, total_time));
        System/*error*/.Console.WriteLine("      Startup        " + timestr(prelim_end - start_time, total_time));
        System/*error*/.Console.WriteLine("      Parse          " + timestr(parse_end - prelim_end, total_time));
        if (check_end != 0)
            System/*error*/.Console.WriteLine("      Checking       " + timestr(check_end - parse_end, total_time));
        if (check_end != 0 && build_end != 0)
            System/*error*/.Console.WriteLine("      Parser Build   " + timestr(build_end - check_end, total_time));
        if (nullability_end != 0 && check_end != 0)
            System/*error*/.Console.WriteLine("        Nullability  " + timestr(nullability_end - check_end, total_time));
        if (first_end != 0 && nullability_end != 0)
            System/*error*/.Console.WriteLine("        First sets   " + timestr(first_end - nullability_end, total_time));
        if (machine_end != 0 && first_end != 0)
            System/*error*/.Console.WriteLine("        State build  " + timestr(machine_end - first_end, total_time));
        if (table_end != 0 && machine_end != 0)
            System/*error*/.Console.WriteLine("        Table build  " + timestr(table_end - machine_end, total_time));
        if (reduce_check_end != 0 && table_end != 0)
            System/*error*/.Console.WriteLine("        Checking     " + timestr(reduce_check_end - table_end, total_time));
        if (emit_end != 0 && build_end != 0)
            System/*error*/.Console.WriteLine("      Code Output    " + timestr(emit_end - build_end, total_time));
        if (Emit.symbols_time != 0)
            System/*error*/.Console.WriteLine("        Symbols      " + timestr(Emit.symbols_time, total_time));
        if (Emit.parser_time != 0)
            System/*error*/.Console.WriteLine("        Parser class " + timestr(Emit.parser_time, total_time));
        if (Emit.action_code_time != 0)
            System/*error*/.Console.WriteLine("          Actions    " + timestr(Emit.action_code_time, total_time));
        if (Emit.production_table_time != 0)
            System/*error*/.Console.WriteLine("          Prod table " + timestr(Emit.production_table_time, total_time));
        if (Emit.action_table_time != 0)
            System/*error*/.Console.WriteLine("          Action tab " + timestr(Emit.action_table_time, total_time));
        if (Emit.goto_table_time != 0)
            System/*error*/.Console.WriteLine("          Reduce tab " + timestr(Emit.goto_table_time, total_time));

        System/*error*/.Console.WriteLine("      Dump Output    " + timestr(dump_end - emit_end, total_time));
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /**
     * Helper routine to format a decimal based display of seconds and percentage of
     * total time given counts of milliseconds. Note: this is broken for use with
     * some instances of negative time (since we don't use any negative time here,
     * we let if be for now).
     * 
     * @param time_val   the value being formatted (in ms).
     * @param total_time total time percentages are calculated against (in ms).
     */
    public static String timestr(long timeVal, long totalTime) {
        bool neg;
        long ms = 0;
        long sec = 0;
        long percent10;
        String pad;

        /* work with positives only */
        neg = timeVal < 0;
        if (neg)
            timeVal = -timeVal;

        /* pull @out seconds and ms */
        ms = timeVal % 1000;
        sec = timeVal / 1000;

        /* construct a pad to blank fill seconds @out to 4 places */
        if (sec < 10)
            pad = "   ";
        else if (sec < 100)
            pad = "  ";
        else if (sec < 1000)
            pad = " ";
        else
            pad = "";

        /* calculate 10 times the percentage of total */
        percent10 = (timeVal * 1000) / totalTime;

        /* build and return the output string */
        return (neg ? "-" : "") + pad + sec + "." + ((ms % 1000) / 100) + ((ms % 100) / 10) + (ms % 10) + "sec" + " ("
            + percent10 / 10 + "." + percent10 % 10 + "%)";
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /** Produce a human readable dump of the grammar. */
    public static void dumpGrammar() /*throws internal_error*/ {
        System/*error*/.Console.WriteLine("===== Terminals =====");
        for (int tidx = 0, cnt = 0; tidx < Vt.Count; tidx++, cnt++) {
            System/*error*/.Console.Write("[" + tidx + "]" + Vt.Find(tidx).Name + " ");
            if ((cnt + 1) % 5 == 0)
                System/*error*/.Console.WriteLine();
        }
        System/*error*/.Console.WriteLine();
        System/*error*/.Console.WriteLine();

        System/*error*/.Console.WriteLine("===== Non terminals =====");
        for (int nidx = 0, cnt = 0; nidx < Vn.Count; nidx++, cnt++) {
            System/*error*/.Console.Write("[" + nidx + "]" + Vn.Find(nidx).Name + " ");
            if ((cnt + 1) % 5 == 0)
                System/*error*/.Console.WriteLine();
        }
        System/*error*/.Console.WriteLine();
        System/*error*/.Console.WriteLine();

        System/*error*/.Console.WriteLine("===== Productions =====");
        for (int pidx = 0; pidx < ProductionDraft.Count; pidx++) {
            ProductionDraft prod = ProductionDraft.Find(pidx);
            System/*error*/.Console.Write("[" + pidx + "] " + prod.lhs.TheSymbolDraft.Name + " ::= ");
            for (int i = 0; i < prod.rhsLength; i++)
                if (prod.rhs(i).IsAction)
                    System/*error*/.Console.Write("{actionPart} ");
                else
                    System/*error*/.Console.Write(((SymbolPart)prod.rhs(i)).TheSymbolDraft.Name + " ");
            System/*error*/.Console.WriteLine();
        }
        System/*error*/.Console.WriteLine();
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /**
     * Produce a (semi-) human readable dump of the complete viable prefix
     * recognition state machine.
     */
    public static void dumpMachine() {
        var ordered = new LALRState[LALRState.Count];

        /* put the states in sorted order for a nicer display */
        foreach (var st in LALRState.AllStates()) {
            ordered[st.Index] = st;
        }

        System/*error*/.Console.WriteLine("===== Viable Prefix Recognizer =====");
        for (int i = 0; i < LALRState.Count; i++) {
            if (ordered[i] == start_state)
                System/*error*/.Console.Write("START ");
            System/*error*/.Console.WriteLine(ordered[i]);
            System/*error*/.Console.WriteLine("-------------------");
        }
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /** Produce a (semi-) human readable dumps of the parse tables */
    public static void dumpTables() {
        System/*error*/.Console.WriteLine(action_table);
        System/*error*/.Console.WriteLine(reduce_table);
    }

    /*-----------------------------------------------------------*/

}
